Skip to main content

Traefik

This section will cover deploying and configuring Traefik using Docker.

Table of Contents
  • Introduction
  • Traefik Setup
  • Traefik Configuration
    • Configuring traefik.yml
    • Configuring dynamic.yaml
    • Configuration Options
      • Routers
      • Middlewares
      • Services
      • Docker Labels
      • Logging
    • Implementing TLS Certificates
  • Example Configurations

Introduction

Traefik is a reverse proxy that is mainly defined using configuration files and Docker labels.

References:

Traefik Setup

The following Docker Compose stack is an example for routing within the same host without HTTPS.

services:
    traefik:
        image: traefik:v3.1
        restart: always
        command:
            - "--api.insecure=true"
            - "--providers.docker"
        ports:
            - 80:80
            - 8080:8080
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock:ro
        labels:
            - "traefik.http.routers.traefik.rule=Host(`traefik.domain.com`)"

#Nginx web server example
    nginx:
        image: nginx:latest
        restart: always
        volumes:
            - ./web:/usr/share/nginx/html:ro
        labels:
            - "traefik.http.routers.nginx.rule=Host(`site.domain.com`)"

Breakdown:

  • --api.insecure=true - Enable the Traefik web UI on HTTP.
  • --providers.docker - Specify Traefik to listen to docker.
  • 80 - HTTP traffic.
  • 8080 - Traefik's web UI.
  • "traefik.http.routers.nginx.rule=Host(`site.domain.com`)" - Specify the rule for routing. An example will be sub domain.

Traefik Configuration

Traefik can also use configuration files - dynamic.yaml and traefik.yml. Configurations can be defined within the dynamic.yaml file for different services on how to route traffic.

The compose.yaml file will need to be modified slightly to allow the use of the configuration files.

services:
    traefik:
        image: traefik:v3.1
        restart: always
        ports:
            - 80:80
            - 8080:8080
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock:ro
            - ./traefik-data/traefik.yml:/etc/traefik/traefik.yml:ro
            - ./traefik-data/dynamic.yaml:/etc/traefik/dynamic.yaml:ro
        labels:
            - "traefik.http.routers.traefik.rule=Host(`traefik.domain.com`)"

#Nginx web server example
    nginx:
        image: nginx:latest
        restart: always
        volumes:
            - ./web:/usr/share/nginx/html:ro
        labels:
            - "traefik.http.routers.nginx.rule=Host(`site.domain.com`)" #Specify the conditions to route the traffic. In this example, sub domain name

The volumes have been added to map the traefik.yml and dynamic.yaml files accordingly. As items such as enabling the dashboard is defined in the traefik.yml file, we can omit the command fields.

Configuring traefik.yml

The traefik.yml file is a static configuration file.

In the traefik.yml file, we can define entry points, which are basically incoming ports. We will also need to define the dynamic.yaml directory and any other providers such as Docker within the file.

entryPoints:
    web:
        address: ":80" #Expose port 80

providers:
    docker: #Allow local Docker routing
        endpoint: "unix:///var/run/docker.sock"
    file:
        directory: "/etc/traefik" #Specify the dynamic.yaml file directory

#Enable Traefik dashboard
api:
    dashboard: true
    insecure: true

Configuring dynamic.yaml

Once the traefik.yml file is configured, we can specify all services in the dynamic.yaml file. An example will be where a website is running on a different host on port 5050.

http:
    routers:
        router0:
            rule: "Host(`site-1.domain.com`)" #Specify the conditions to route.
            service: service-a #Specify the service name in the "services" section
            entryPoints:
                - web #Specify the incoming ports defined in the traefik.yml file

    services:
        service-a:
            loadBalancer:
                servers:
                    - url: "http://10.10.10.2:5050" #Specify the URL of the target service

The above dynamic.yaml configuration will allow the website at http://10.10.10.2:5050 to be accessed with the site-1.domain.com domain.

Configuration Options

This section will cover some common configuration options for the different items in Traefik.

Routers

The following will list some common rules for the dynamic.yaml file. The rules will define if a particular request matches a specific criteria.

RuleDescriptionExample
Host(`domain`)Matches requests based on the host domain.Host(`site.domain.com`)
Method(`<method>`)Matches requests based on the method.Method(`GET`)
Path(`<path`)Matches requests based on the path.Path(`/home/dashboard`)
ClientIP(`IP`)Matches requests based on the client IP. Accepts IPv4, IPv6, and CIDR formats.ClientIP(`10.10.5.0/24`)

More information can be found in the official documentation.

https://doc.traefik.io/traefik/routing/routers/#rule

Middlewares

Middleware in Traefik are attached to routers and are a means of tweaking requests before they are being sent to the service. This section will cover some useful middleware and their configuration method.

To configure an IP allow list, we can use the following in the dynamic.yaml file.

http:
middlewares:
middleware1:
ipAllowList:
sourceRange:
- 10.10.10.0/24
- 172.30.1.0/10
- 192.168.1.2

Using Docker labels:

labels:
- "traefik.http.middlewares.my-ipallowlist.ipallowlist.sourcerange=127.0.0.1/32, 192.168.1.2"

To configure basic authentication, we can use the BasicAuth middleware. This allows for authentication before accessing a particular service. We can use the following to do this in the dynamic.yaml file.

Note that the hash must be either MD5 (not recommended), SHA1, or BCrypt. The hashes can be generated using htpasswd on Linux.

http:
middlewares:
middleware02:
basicAuth:
users:
- "<username>:<password hash>"
- "<username>:<password hash>"
- "test:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"

Alternatively, an external file that contains the credentials with a username:password-hash format can be used. It can be passed into Traefik with the usersFile.

http:
middlewares:
middleware02:
basicAuth:
usersFile: "/path/to/userfile"

Using Docker labels:

labels:
- "traefik.http.middlewares.auth-1.basicauth.users=username:password-hash,test:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"

More information can be found in the official Documentation:

https://doc.traefik.io/traefik/middlewares/overview/
https://doc.traefik.io/traefik/middlewares/http/ipallowlist/
https://doc.traefik.io/traefik/middlewares/http/basicauth/

Services

The Services are responsible for configuring how to reach the actual services that will eventually handle the incoming requests. The following options are defined in the dynamic.yaml file.

If a application has multiple instances, it is possible to configure load balancing between the instances. Note that the following configurations also applies to a single instance even if no load balancing is required.

For HTTP:

http:
<router configuration>

services:
<service-name>:
loadBalancer:
servers:
- url: "http://<IP>:<PORT>"
- url: "http://<IP>:<PORT>"

For TCP or UDP:

tcp/udp: #Remove either TCP or UDP accordingly
<router configuration>

services:
<service-name>
loadBalancer:
servers:
- address: "<IP>:<PORT>"
- address: "<IP>:<PORT>"

If load balancing is used, we can use weight to specify weighted load balancing. By default, Traefik uses round robin.

http:
<router configuration>

services:
<service-name>:
loadBalancer:
servers:
- url: "http://10.100.2.1:8000"
weight: 2
- url: "http://10.100.2.2:8000"
weight: 1

More information can be found in the official documentation.

https://doc.traefik.io/traefik/routing/services/

Docker Labels

If there are applications running with Docker containers on the same host as Traefik, Docker labels can be used.

The following will be some common labels that can be used.

services:
website:
#container configuration
labels:
#Match the specified domain to the service
- traefik.http.routers.website.rule=Host(`domain.com`)
#Specify a custom port for the container
- traefik.http.services.website.loadbalancer.server.port=5123

It is also possible to assign a container more than one router and service.

services:
website:
#container configuration
labels:
- traefik.http.routers.web-a.rule(`site.domain.com`)
- traefik.http.routers.web-a.service=web-a-service
- traefik.http.services.web-a-service.loadbalancer.server.port=8080

- traefik.http.routers.admin-web.rule(`admin.domain.com`)
- traefik.http.routers.admin-web.service=admin-service
- traefik.http.services.admin-service.loadbalancer.server.port=2200

More information can be found in the official documentation.

https://doc.traefik.io/traefik/routing/providers/docker/

Logging

To enable logging, we can specify the path to store either traefik logs, access logs, or both in the traefik.yml file.

log: #Store Traefik events
filePath: "/var/log/traefik/traefik.log"
maxSize: 500 #Specify the max file size in Megabytes
maxBackups: 3 #Specify the number of backups to keep

accessLog: #Store access logs
filePath: "/var/log/traefik/access.log"

Once added, we can map the /var/log/traefik to a directory on the host in the compose.yaml file.

volumes:
- ./traefik-data/logs:/var/log/traefik

Implementing TLS Certificates

To use custom TLS certificates, we can define them in the dynamic.yaml file.

tls:
certificates:
- certFile: /path/to/domain.cert
keyFile: /path/to/domain.key
- certFile: /path/to/additional-domain.cert
keyFile: /path/to/additional-domain.key

It is also possible to use a provider such as Let's Encrypt to get wildcard certificates using the following configuration in the traefik.yml file.

entryPoints:
websecure:
address: ":443"
http:
tls:
certResolver: my-resolver
domains:
- main: "my.domain.com"
sans:
- "*.my.domain.com"

certificatesResolvers:
my-resolver:
acme:
email: "example@email.com" #Email for registration
storage: "acme.json"
dnsChallenge:
provider: dns-provider-here

Once set, we can use tls: {} to terminate the TLS requests on the router (proxy) and use HTTP to communicate with the configured service if required.

http:
routers:
router1:
rule: "Host(`domain.com`)"
servcice: myweb
tls: {}

More information can be found in the official documentation.

https://doc.traefik.io/traefik/https/tls/
https://doc.traefik.io/traefik/https/acme/
https://doc.traefik.io/traefik/routing/routers/#tls_1

Example Configurations

compose.yaml:

services:
    traefik:
        image: traefik:v3.1
        restart: always
        #command:
            #- "--api.insecure=true" #Enable the web UI on HTTP. Not required if using traefik.yml
            #- "--providers.docker" #Listen to Docker. Not required if using traefik.yml
        ports:
            - "80:80"
            - "443:443"
            #- "8080:8080" #Traefik dashboard
            #Additional ports if required
            #- "25565:25565/udp"
            #- "25565:25565"
        volumes:
            - /etc/localtime:/etc/localtime:ro #Set the timezone for Traefik
            - /var/run/docker.sock:/var/run/docker.sock:ro
            - ./traefik-data/traefik.yml:/etc/traefik/traefik.yml:ro #Not required for simple single host deployment
            - ./traefik-data/dynamic.yaml:/etc/traefik/dynamic.yaml:ro #Not required for simple single host deployment
            - ./traefik-data/user-creds:/etc/traefik/user-creds:ro #Pass user credentials for BasicAuth "username:password-hash" format
            - ./traefik-data/acme:/etc/traefik/acme #Used to store TLS certificates if using Let's Encrypt
            - ./traefik-data/logs:/var/log/traefik/ #Used for storing logs
        environment:
            - CF_DNS_API_TOKEN=api_token_change_me #Used for DNS challenge for Let's Encrypt

#Nginx with load balancing example
    nginx:
        image: nginx:latest
        restart: always
        volumes:
            - ./web:/usr/share/nginx/html:ro
        deploy:
            mode: replicated
            replicas: 2
        labels:
            - "traefik.enable=true"
            - "traefik.http.routers.nginx.rule=Host(`site.domain.com`)" #Specify the conditions to route the traffic. In this example, sub domain name
            - "traefik.http.routers.nginx.entrypoints=websecure"
            - "traefik.http.routers.nginx.tls=true" #Enable TLS
            - "traefik.http.routers.nginx.middlewares=my-ipallowlist" #Specify the middlewares to use.
            - "traefik.http.middlewares.my-ipallowlist.ipallowlist.sourcerange=127.0.0.1, 172.16.1.0/24, 192.168.0.0/16" #Specify allowed IP addresses

traefik.yml:

entryPoints:
    web:
        address: ":80"
        http:
            redirections: #Redirect all traffic from port 80 to 443
            entryPoint:
                to: websecure
                scheme: https
                permanent: true

    websecure:
        address: ":443"
        asDefault: true #Set port 443 as the default port
        reusePort: true
        http:
            tls: #Wildcard certificate setup using cloudflare
                certResolver: cf-resolver
                domains:
                    - main: "my.domain.com"
                        sans:
                            - "*.my.domain.com"

    minecraft:
        address: ":25565"
    minecraft-udp:
        address: ":25565/udp"

providers:
    docker: #Allow local Docker routing
        endpoint: "unix:///var/run/docker.sock"
    file:
        directory: "/etc/traefik" #Specify the dynamic.yaml file directory
        watch: true #Specify to watch the file for updates.

#Enable logging
log:
    filePath: "/var/log/traefik/traefik.log"
    maxSize: 500 #Specify the max file size
    maxBackups: 3 #Specify the number of backups to keep

accessLog:
    filePath: "/var/log/traefik/access.log"

#Enable Traefik dashboard
api:
    dashboard: true
    insecure: false

global:
    checkNewVersion: true
    sendAnonymousUsage: false

#TLS Certificates
certificatesResolvers:
    my-resolvers:
        acme:
            email: "example@email.com" #Email for registration with Let's Encrypt
            storage: "acme.json"
            dnsChallenge:
                provider: dns-provider-name #Specify the DNS provider for the DNS challenge
#Cloudflare
    cf-resolver:
        acme:
            email: "change_me@email.com"
            storage: "acme.json"
            dnsChallenge:
                provider: cloudflare
                resolvers:
                    - "1.1.1.1:53"
                    - "1.0.0.1:53"
                #disablePropagationCheck: true

dynamic.yaml:

http:
    routers:
        router0:
            rule: "Host(`site-1.domain.com`)" #Specify the conditions to route. An example will be using the subdomain name
            service: service-a #Specify the service name in the "services" section
            entryPoints:
                - web #Specify the incoming ports defined in the traefik.yml file

        router1:
            rule: "Host(`site-2.domain.com`)"
            service: service-b
            entryPoints:
                - websecure
            tls: {} #Specify to terminate TLS requests on the router (proxy)
        dashboard-router: #Traefik dashboard
            rule: "Host(`proxy.domain.com`) && Path(`/dashboard/`)"
            service: api@internal
            middlewares:
                - middleware01
                - middleware02
            entryPoints:
                - websecure
            tls: {}

    services:
        service-a:
            loadBalancer:
                servers:
                    - url: "http://<IP>:<PORT>" #Specify the URL of the target service

        service-b:
            loadBalancer:
                servers:
                    - url: "http://<IP>:<PORT>"
                    - url: "http://<IP>:<PORT>"

    middlewares:
        middlware01:
                ipAllowList:
                    sourceRange: #Specify the IP addresses that are allowed to use the service
                        - 10.10.10.0/24
                        - 127.0.0.1/32
                        - 192.168.1.1

        middleware02:
            basicAuth:
                users:
                    - "username:hashed-password"
                    - "test:$apr1$aKKMfSlx$.ylkR36/LnWluAJeIWkwk0"
                    - "myuser:$apr1$Chh2tzN4$EtSpG/GkIGM/x8lfj3Jdx1"
                #usersFile: "/etc/traefik/user-creds" #Uncomment if using a file to specify user credentials

tcp:
    routers:
        tcprouter0:
            rule: "ClientIP(`0.0.0.0/0`)"
            service: mc
            entryPoints:
                - minecraft

        services:
            mc:
                loadBalancer:
                    servers:
                        - address: "10.10.10.10:25565"

udp:
    routers:
        minecraft-router:
            service: service-udp-a
            entryPoints:
                - minecraft-udp

    services:
        service-udp-a:
            loadBalancer:
                servers:
                    - address: "10.10.10.10:25565"

#Not required if using Let's Encrypt
tls:
    certificates:
        - certFile: /path/to/domain.cert
          keyFile: /path/to/domain.key